﻿using System;
using System.Threading;
using ExtractItAll.Extractors;

namespace ExtractItAll.Core
{
  /// <summary>
  /// Executes a single task and updates the manager about the progress.
  /// </summary>
  public class TaskExecutor
  {
    private TaskManager _Manager;
    private TaskItem _Task;
    private Thread _Worker;
    private BaseExtractor extractor;

    /// <summary>
    /// Creates a new instance of <see cref="TaskExecutor" /> class.
    /// </summary>
    /// <param name="manager">Task manager to update.</param>
    /// <param name="task">Task to execute.</param>
    public TaskExecutor(TaskManager manager, TaskItem task)
    {
      _Manager = manager;
      _Task = task;
      _Worker = null;
      extractor = null;
    }

    /// <summary>
    /// Gets executed task item.
    /// </summary>
    public TaskItem Task
    {
      get { return _Task; }
    }

    /// <summary>
    /// Starts execution.
    /// </summary>
    public void Execute()
    {
      _Worker = new Thread(Work);
      _Worker.Priority = Program.Settings.ThreadPriority;
      _Worker.Start();
    }

    /// <summary>
    /// Cancels execution.
    /// </summary>
    public void Cancel()
    {
      if (extractor != null)
      {
        extractor.Canceled = true;
      }
      _Worker.Join(500);
    }

    /// <summary>
    /// Does the real work.
    /// </summary>
    private void Work()
    {
      Program.Passwords.Reset();

      if (!_Task.Archive.FileInfo.Exists)
      {
        _Manager.TaskError("File doesn't exist");
        return;
      }
      else
      {
        extractor = (BaseExtractor)Activator.CreateInstance(Task.ExtracorType);
      }

      if (extractor == null)
        return;

      _Manager.RaiseArchiveProgressChanged(new ProgressInformation());

      // initialization of the extractor
      if (!extractor.Initialize(_Task.Archive))
      {
        _Task.Password = extractor.Message;
        _Manager.TaskError("Error");
        extractor.Close();
        return;
      }

      // try to extract without a password
      if (extractor.Extract(null))
      {
        _Manager.TaskFinished();
        extractor.Close();
        return;
      }
      if (extractor.Canceled)
      {
        if (!String.IsNullOrEmpty(extractor.Message))
        {
          _Task.Password = extractor.Message;
          _Manager.TaskError("Error");
        }
        else
        {
          _Manager.TaskWarning("Canceled");
        }
        extractor.Close();
        return;
      }

      //extraction by using the passwordlist
      while (true)
      {
        Task.Password = Program.Passwords.GetNextPassword();
        if (Task.Password == null)
          Task.Password = String.Empty;
        _Manager.RaiseArchiveProgressChanged(new ProgressInformation());

        if (extractor.Extract(Task.Password))
        {
          Program.Passwords.SetCorrectPassword(Task.Password);
          _Manager.TaskFinished();
          break;
        }

        if (Program.Passwords.AllPasswordsChecked)
        {
          _Manager.TaskWarning("Password not found");
          _Task.Password = String.Empty;
          break;
        }

        if (extractor.Canceled)
        {
          if (!String.IsNullOrEmpty(extractor.Message))
          {
            _Task.Password = extractor.Message;
            _Manager.TaskError("Error");
          }
          else
          {
            _Manager.TaskWarning("Canceled");
          }
          break;
        }
      }

      extractor.Close();
    }
  }
}